const twgl = require("twgl.js");
const Skin = require("./Skin");

const DEFAULT_CHECKER_STYLE = {
    axis: {
        lineWidth: 1,
        strokeStyle: "#d8d8d9",
    },
    point: {
        fillStyle: "#d8d8d9",
    },
};

class CoordinateSkin extends Skin {
    constructor(id, renderer) {
        super(id);
        this._renderer = renderer;
        this._canvas = document.createElement("canvas");
        this._size = null;
        this._renderedScale = 0;
        this._textureDirty = true;
        this.ctx = null;
        this.checkerStyle = DEFAULT_CHECKER_STYLE;
        this._fontSize = 14;
        this._setCanvasSize(renderer.getNativeSize());
    }
    get size() {
        return this._size;
    }
    set fontSize(value) {
        this._fontSize = value;
    }
    dispose() {
        if (this._texture) {
            this._renderer.gl.deleteTexture(this._texture);
            this._texture = null;
        }
        this._canvas = null;
        super.dispose();
    }
    _setCanvasSize(canvasSize) {
        const [width, height] = canvasSize;
        this._size = canvasSize;
        this._rotationCenter[0] = width / 2;
        this._rotationCenter[1] = height / 2;
    }
    _setCtxAttributes(attributes) {
        for (const key in attributes) {
            this.ctx[key] = attributes[key];
        }
    }
    _drawLine(x0, y0, x1, y1) {
        this.ctx.beginPath();
        this.ctx.moveTo(
            this._rotationCenter[0] + x0,
            this._rotationCenter[1] + y0
        );
        this.ctx.lineTo(
            this._rotationCenter[0] + x1,
            this._rotationCenter[1] + y1
        );
        this.ctx.stroke();
    }
    _drawText(text, x, y) {
        this.ctx.fillText(
            text,
            this._rotationCenter[0] + x,
            this._rotationCenter[1] + y
        );
    }
    _drawAxis(axisData) {
        const { coordinates, coordPoints, attributes } = axisData;
        this._setCtxAttributes(attributes.axis);
        for (let index = 0; index < coordinates.length; index++) {
            const item = coordinates[index];
            const { x0, y0, x1, y1 } = item;

            this._drawLine(x0, y0, x1, y1);
        }
        this.ctx.font = `${this._fontSize}px sans-serif`;
        this.ctx.textBaseline = "top";
        this.ctx.textAlign = "center";
        this._setCtxAttributes(attributes.point);
        for (let index = 0; index < coordPoints.length; index++) {
            const item = coordPoints[index];
            const { text, x, y } = item;

            this._drawText(text, x, y);
        }
    }
    _getMainAxis() {
        const maxX = this._size[0];
        const maxY = this._size[1];
        const axisAttributes = this.checkerStyle.axis;
        const pointAttributes = this.checkerStyle.point;
        return {
            coordinates: [
                {
                    x0: maxX,
                    y0: 0,
                    x1: -maxX,
                    y1: 0,
                },
                {
                    x0: 0,
                    y0: maxY,
                    x1: 0,
                    y1: -maxY,
                },
            ],
            coordPoints: [
                {
                    text: 0,
                    x: 0,
                    y: 0,
                },
            ],
            attributes: {
                axis: {
                    strokeStyle: axisAttributes.strokeStyle,
                    lineWidth: axisAttributes.lineWidth * 2,
                },
                point: pointAttributes,
            },
        };
    }
    _getGridAxis(interval = 100, isNeedCoordPoint = true) {
        const maxX = this._size[0];
        const maxY = this._size[1];
        const axisAttributes = this.checkerStyle.axis;
        const pointAttributes = this.checkerStyle.point;
        const coordinates = [];
        const coordPoints = [];
        let stepX = interval;
        let stepY = interval;
        while (stepX < maxX) {
            coordinates.push({
                x0: stepX,
                y0: maxY,
                x1: stepX,
                y1: -maxY,
            });
            coordinates.push({
                x0: -stepX,
                y0: maxY,
                x1: -stepX,
                y1: -maxY,
            });
            coordPoints.push({
                text: stepX,
                x: stepX,
                y: 0,
            });
            coordPoints.push({
                text: -stepX,
                x: -stepX,
                y: 0,
            });
            stepX += interval;
        }
        while (stepY < maxY) {
            coordinates.push({
                x0: maxX,
                y0: stepY,
                x1: -maxX,
                y1: stepY,
            });
            coordinates.push({
                x0: maxX,
                y0: -stepY,
                x1: -maxX,
                y1: -stepY,
            });
            coordPoints.push({
                text: -stepY,
                x: 0,
                y: stepY,
            });
            coordPoints.push({
                text: stepY,
                x: 0,
                y: -stepY,
            });
            stepY += interval;
        }
        return {
            coordinates,
            coordPoints: isNeedCoordPoint ? coordPoints : [],
            attributes: {
                axis: {
                    strokeStyle: axisAttributes.strokeStyle,
                    lineWidth: isNeedCoordPoint
                        ? axisAttributes.lineWidth
                        : axisAttributes.lineWidth * 0.7,
                },
                point: pointAttributes,
            },
        };
    }
    _render(scale) {
        this.ctx = this._canvas.getContext("2d");
        this._canvas.width = Math.ceil(this._size[0]);
        this._canvas.height = Math.ceil(this._size[1]);
        const mainAxis = this._getMainAxis();
        const unitAxis = this._getGridAxis();
        const detailsAxis = this._getGridAxis(20, false);
        this._drawAxis(mainAxis);
        this._drawAxis(unitAxis);
        this._drawAxis(detailsAxis);
    }

    getTexture(scale) {
        if (this._textureDirty) {
            this._render();
            this._textureDirty = false;
            const textureData = this.ctx.getImageData(
                0,
                0,
                this._canvas.width,
                this._canvas.height
            );
            const gl = this._renderer.gl;
            if (this._texture === null) {
                const textureOptions = {
                    auto: false,
                    wrap: gl.CLAMP_TO_EDGE,
                };
                this._texture = twgl.createTexture(gl, textureOptions);
            }
            this._setTexture(textureData);
        }
        return this._texture;
    }
}

module.exports = CoordinateSkin;
